////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2011-2012 www.iflytek.com. All Rights Reserved.
//  This software for customer relationship management system, developed by Iflytek-ZY-GCZX team.
//  Software code and design for the team, copy rights reserved.
////////////////////////////////////////////////////////////////////////////////

package com.iflytek.ci.main.utils;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * 字符串日期格式换数.
 * </p>
 *
 * @author poplar.yfyang
 * @version 1.0 2012-11-18 12:36 PM
 * @since JDK 1.5
 */
public class Dates {

	/** 时间格式 */
	public static final String TIME_FORMAT = "00:00:00";
	/** 每天结束的时间 */
	public static final String DAY_END_TIME = "23:59";
	/** 每天开始的时间 */
	public static final String DAY_BEGIN_TIME = "00:00";
	/** 输出本类的相关log日志 */
	protected static final Logger logger = LoggerFactory.getLogger(Dates.class);
	/** 0 */
	private static final String ZERO = "0";
	/** 空白符号 */
	private static final String BLOCK = "";

	/**
	 * 返回字符串的字节数
	 *
	 * @param str 字符串
	 */
	public static int getStrByteNum(String str) {
		return str != null ? str.getBytes().length : 0;
	}

	/**
	 * 截取指定字节数的字符串，此方法避免把汉字一分为二
	 *
	 * @param str 字符串
	 */
	public static String getCutStrByByte(String str, int chinaSize) {
		int byteSize = chinaSize * 2;
		int byteNum = getStrByteNum(str);

		// 字节数少于指定字节数，直接返回
		if (byteNum <= byteSize) {
			return str;
		}

		int len = str.length();
		String result = BLOCK;
		for (int i = chinaSize; i < len; i++) {
			String temp = str.substring(0, i);
			if (getStrByteNum(temp) > byteSize) {
				break;
			} else {
				result = temp;
			}
		}

		return result;
	}

	/**
	 * 把string类型的日期转化为相应的格式
	 *
	 * @param dateString 字符串日期
	 * @param formatNum  需要按照哪种类型
	 *                   1 yyyyMMdd
	 *                   2 yyyy/MM/dd
	 *                   3 yyyy-MM-dd
	 *                   4 yyyy-MM-dd HH:mm:ss
	 *                   5 yyyyMMddHHmmss
	 *                   6 yyyy-MM-dd HH:mm:ss.sss
	 *                   7 HH:mm
	 *                   8 yyyy年MM月dd日
	 *                   9 yyyyMMddHHmmssSSS
	 *                   10 yyyy年MM月dd日 HH:mm
	 *                   11 yyyy-MM-dd HH
	 *                   12 HH:mm:ss
	 * @return 符合格式的日期
	 */
	public static Date stringToDate(String dateString, int formatNum) {
		Date date = null;
		if (StringUtils.isNotBlank(dateString)) {
			try {
				date = dateFormat(formatNum).parse(dateString);
			} catch (ParseException e) {
				date = null;
				logger.error("StringToDate转换错误", e);
			}
		}

		return date;
	}

	/**
	 * 把string类型的日期转化为相应的格式
	 *
	 * @param date      字符串日期
	 * @param formatNum 1 yyyyMMdd
	 *                  2 yyyy/MM/dd
	 *                  3 yyyy-MM-dd
	 *                  4 yyyy-MM-dd HH:mm:ss
	 *                  5 yyyyMMddHHmmss
	 *                  6 yyyy-MM-dd HH:mm:ss.sss
	 *                  7 HH:mm
	 *                  8 yyyy年MM月dd日
	 *                  9 yyyyMMddHHmmssSSS
	 *                  10 yyyy年MM月dd日 HH:mm
	 *                  11 yyyy-MM-dd HH
	 *                  12 HH:mm:ss
	 *                  14 yyyy-MM-ddTHH:mm:ss+08:00
	 *                  15 yyyy-MM-dd HH:mm
	 *                  16 yyyyMM
	 *                  17 yyyy年MM月dd日hh时mm分
	 * @return 字符串日期
	 */
	public static String dateToString(Date date, int formatNum) {
		String str = null;
		if (date != null) {
			// return parseString(date, formatNum);
			str = dateFormat(formatNum).format(date);
		}
		return str;
	}

	/**
	 * 实例SimpleDateFormat
	 *
	 * @param formatNum 1 yyyyMMdd
	 *                  2 yyyy/MM/dd
	 *                  3 yyyy-MM-dd
	 *                  4 yyyy-MM-dd HH:mm:ss
	 *                  5 yyyyMMddHHmmss
	 *                  6 yyyy-MM-dd HH:mm:ss.sss
	 *                  7 HH:mm
	 *                  8 yyyy年MM月dd日
	 *                  9 yyyyMMddHHmmssSSS
	 *                  10 yyyy年MM月dd日 HH:mm
	 *                  11 yyyy-MM-dd HH
	 *                  12 HH:mm:ss
	 *                  17 yyyy年MM月dd日hh时mm分
	 * @return 格式日期
	 */
	private static SimpleDateFormat dateFormat(int formatNum) {
		SimpleDateFormat dateFormatter = null;
		switch (formatNum) {
			case 1:
				dateFormatter = new SimpleDateFormat("yyyyMMdd");
				break;
			case 2:
				dateFormatter = new SimpleDateFormat("yyyy/MM/dd");
				break;
			case 3:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
				break;
			case 4:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				break;
			case 5:
				dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
				break;
			case 6:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss");
				break;
			case 7:
				dateFormatter = new SimpleDateFormat("HH:mm");
				break;
			case 8:
				dateFormatter = new SimpleDateFormat("yyyy年MM月dd日");
				break;
			case 9:
				dateFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
				break;
			case 10:
				dateFormatter = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
				break;
			case 11:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH");
				break;
			case 12:
				dateFormatter = new SimpleDateFormat("HH:mm:ss");
				break;
			case 14:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+08:00");
				break;
			case 15:
				dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
				break;
			case 16:
				dateFormatter = new SimpleDateFormat("yyyyMM");
				break;
			case 17:
				dateFormatter = new SimpleDateFormat("yyyy年MM月dd日HH时mm分");
				break;
		}
		return dateFormatter;
	}

	/**
	 * 按指定格式返回时间,实际是舍弃某些格式
	 * <p/>
	 * （入参是Date对象，出参也是Date对象，比如 date是new Date，则
	 * dateFormat(date,1)返回的是时分秒为0的date）
	 *
	 * @param date      日期
	 * @param formatNum 1 yyyyMMdd
	 *                  2 yyyy/MM/dd
	 *                  3 yyyy-MM-dd
	 *                  4 yyyy-MM-dd HH:mm:ss
	 *                  5 yyyyMMddHHmmss
	 *                  6 yyyy-MM-dd HH:mm:ss.sss
	 *                  7 HH:mm
	 *                  8 yyyy年MM月dd日
	 *                  9 yyyyMMddHHmmssSSS
	 *                  10 yyyy年MM月dd日 HH:mm
	 *                  11 yyyy-MM-dd HH
	 *                  12 HH:mm:ss
	 * @return 按指定格式返回时间
	 */
	public static Date dateFormat(Date date, int formatNum) {
		return stringToDate(dateToString(date, formatNum), formatNum);
	}

	/**
	 * 给一个日期加上N天或减去N天得到一个新的日期
	 *
	 * @param startDate 需要增加的日期时间
	 * @param add_no    添加的天数，可以是正数也可以是负数
	 * @return 操作后的日期
	 */
	public static Date addDay(Date startDate, int add_no) {
		if (startDate == null) {
			startDate = new Date();
		}

		Calendar cc = Calendar.getInstance();
		cc.setTime(startDate);
		cc.add(Calendar.DATE, add_no);
		return cc.getTime();
	}

	/**
	 * 给一个日期加上N小时或减去N小时得到一个新的日期
	 *
	 * @param baseTime    需要增加的日期时间
	 * @param amountToAdd 添加的小时数，可以是正数也可以是负数
	 * @return 操作后的日期
	 */
	public static Date addHours(Date baseTime, int amountToAdd) {
		if (baseTime == null) {
			baseTime = new Date();
		}
		Calendar cc = Calendar.getInstance();
		cc.setTime(baseTime);
		cc.add(Calendar.HOUR, amountToAdd);
		return cc.getTime();
	}

	/**
	 * 给一个字符格式的日期加上N天或减去N天得到一个新的日期
	 *
	 * @param stringDate 需要增加的日期时间
	 * @param add_no     添加的天数，可以是正数也可以是负数
	 * @return 操作后的日期
	 */
	public static String addDay(String stringDate, int add_no) {
		int f = 3;
		if (stringDate.contains(":")) {
			f = 4;
		}
		Date date = addDay(stringToDate(stringDate.trim(), f), add_no);
		return dateToString(date, f);
	}

	/**
	 * 给一个时间加上N分种或减去N分种后得到一个新的日期
	 *
	 * @param startDate 需要增加的日期时间
	 * @param add_no    添加的分钟数，可以是正数也可以是负数
	 * @return 操作后的日期
	 */
	public static Date addMinute(Date startDate, int add_no) {
		Calendar cc = Calendar.getInstance();
		if (startDate != null) {
			cc.setTime(startDate);
			cc.add(Calendar.MINUTE, add_no);
			return cc.getTime();
		} else {
			return null;
		}
	}

	/**
	 * 添加月份
	 *
	 * @param startDate 日期
	 * @param add_no    添加的月数
	 * @return 添加后的日期
	 */
	public static Date addMonth(Date startDate, int add_no) {
		Calendar cc = Calendar.getInstance();
		if (startDate != null) {
			cc.setTime(startDate);
			cc.add(Calendar.MONTH, add_no);
			return cc.getTime();
		} else {
			return null;
		}
	}

	/**
	 * 得到当前年份
	 *
	 * @return 得到当前年份
	 */
	public static int getNowYear() {
		Calendar cc = Calendar.getInstance();
		return cc.get(Calendar.YEAR);
	}

	/**
	 * 得到当前月份
	 *
	 * @return 当前月份
	 */
	public static int getNowMonth() {
		Calendar cc = Calendar.getInstance();
		return cc.get(Calendar.MONTH) + 1;
	}

	/**
	 * 比较两个日期的相差毫秒数,如果开始日期比结束日期早，则返回正数，否则返回负数。
	 *
	 * @param start 开始日期
	 * @param end   结束日期
	 * @return 如果开始日期比结束日期早，则返回正数，否则返回负数
	 */
	public static long compareDate(Date start, Date end) {
		Calendar starts = Calendar.getInstance();
		Calendar ends = Calendar.getInstance();
		starts.setTime(start);
		ends.setTime(end);
		return ends.getTime().getTime() - starts.getTime().getTime();
	}

	/**
	 * 比较两个日期的相差天数,如果开始日期比结束日期早，则返回正数，否则返回负数。
	 *
	 * @param start 开始日期
	 * @param end   结束日期
	 * @return 如果开始日期比结束日期早，则返回正数，否则返回负数
	 */
	public static long compareDay(Date start, Date end) {
		long day = compareDate(start, end);
		return day / 1000 / 60 / 60 / 24;
	}

	/**
	 * 比较两个日期的相差天数,如果开始日期比结束日期早，则返回正数，否则返回负数。 0天返回0、小于等于一天返回1、大于一天返回2、依次类推
	 *
	 * @param start 开始日期
	 * @param end   结束日期
	 * @return 如果开始日期比结束日期早，则返回正数，否则返回负数。 0天返回0、小于等于一天返回1、大于一天返回2、依次类推
	 */
	public static long dayDiff(Date start, Date end) {
		long day = compareDate(start, end);
		if (day == 0) {
			return 0;
		}
		if (day > 0) {
			return (day - 1) / 1000 / 60 / 60 / 24 + 1;
		} else {
			return (day + 1) / 1000 / 60 / 60 / 24 - 1;
		}

	}

	/**
	 * 得到一个日期的星期几数，星期日返回1以此类推,与真实的要减去1
	 *
	 * @param date 日期
	 * @return 一个日期的星期几数，星期日返回1以此类推,与真实的要减去1
	 */
	public static int getDateIsWeek(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		return c.get(Calendar.DAY_OF_WEEK);
	}

	/**
	 * 返回一个日期的星期字符串
	 *
	 * @param date 日期
	 * @return 一个日期的星期字符串
	 */
	public static String getDateToWeek(Date date) {
		int w = getDateIsWeek(date);
		String week = "星期";
		switch (w) {
			case 1:
				week += "日";
				break;
			case 2:
				week += "一";
				break;
			case 3:
				week += "二";
				break;
			case 4:
				week += "三";
				break;
			case 5:
				week += "四";
				break;
			case 6:
				week += "五";
				break;
			case 7:
				week += "六";
				break;
		}
		return week;
	}

	/**
	 * 根据日期得到入司时长<br>
	 * 1-3月=1;3-6月=3;6-12月=6;12-24月=12;24月以上=24;
	 * 请注意，如果一个员工是2008-01-01进入公司的，那么在2008
	 * -04-01那天仍然算是(1-3月),2008-04-02就算是(3-6月)的.
	 *
	 * @param startWorkTime 入职时间
	 * @return 1, 3, 6, 12, 24
	 */
	public static int getJoinTime(Date startWorkTime) {
		if (startWorkTime == null) {
			return 1;
		}
		Date nowDate = dateFormat(addDay(new Date(), 1), 3);
		Date new_date = addMonth(startWorkTime, 3);
		long time = compareDate(new_date, nowDate);
		if (time > 0) {
			// 超过3个月
			new_date = addMonth(startWorkTime, 6);
			time = compareDate(new_date, nowDate);
			if (time > 0) {
				new_date = addMonth(startWorkTime, 12);
				time = compareDate(new_date, nowDate);
				if (time > 0) {
					new_date = addMonth(startWorkTime, 24);
					time = compareDate(new_date, nowDate);
					if (time > 0) {
						return 24;
					} else {
						return 12;
					}
				} else {
					return 6;
				}
			} else {
				return 3;
			}
		} else {
			return 1;
		}
	}

	/**
	 * 提供精确的加法运算。
	 *
	 * @param v1 被加数
	 * @param v2 加数
	 * @return 两个参数的和
	 */
	public static double doubleAdd(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return (b1.add(b2)).doubleValue();
	}

	/**
	 * 将DOUBLE类型的数据进行格式化
	 *
	 * @param str double类型数据
	 * @param max 保存小数点后最多位数据
	 * @param min 如果小数点后都为0，则保留最小位数
	 * @return 数据进行格式化
	 */
	public static String numberFormat(double str, int max, int min) {
		NumberFormat nf = NumberFormat.getInstance();
		nf.setMaximumFractionDigits(max);
		nf.setMinimumFractionDigits(min);
		return nf.format(str);
	}

	/**
	 * 将string类型转化为double类型,默认为0
	 *
	 * @param double_str double类型
	 */
	public static double stringToDouble(String double_str) {
		double d = 0d;
		if (double_str == null || double_str.length() == 0) {
			return d;
		}
		try {
			d = Double.parseDouble(double_str);
		} catch (Exception e) {
			return d;
		}
		return d;
	}

	/**
	 * 将日期格式转化为java.sql.Date类型，如果入参为null，则返回null
	 *
	 * @param date 日期
	 * @return 日期格式转化为java.sql.Date类型，如果入参为null，则返回null
	 */
	public static java.sql.Date toSqlDate(Date date) {
		if (date != null) {
			return new java.sql.Date(date.getTime());
		} else {
			return null;
		}
	}

	/**
	 * 将传入的s数转化为时分秒格式
	 *
	 * @param time 时间
	 * @return 将传入的s数转化为时分秒格式
	 */
	public static String convertTime(Long time) {
		if (time == 0) {
			return TIME_FORMAT;
		}
		if (time > 0) {
			long h, hh;
			long m, mm;
			long s;
			h = time / 3600;
			hh = time % 3600;
			m = hh / 60;
			mm = hh % 60;
			s = mm;
			String hStr = (h < 10 ? ZERO : BLOCK) + h;
			String mStr = (m < 10 ? ZERO : BLOCK) + m;
			String sStr = (s < 10 ? ZERO : BLOCK) + s;

			return hStr + ":" + mStr + ":" + sStr;
		}
		return TIME_FORMAT;
	}

	/**
	 * 判断字符串是否是Double
	 *
	 * @param str 字符串    z
	 * @return 如果是Double，则返回true
	 */
	public static boolean isDouble(String str) {
		Pattern pattern = Pattern.compile("^[-\\+]?\\d*(\\.\\d*)?");
		return pattern.matcher(str).matches();
	}

	/**
	 * 根据分割符，把字符串转化集合Set
	 *
	 * @param str   字符串
	 * @param split 分割符号
	 * @return 把字符串转化集合Set
	 */
	public static Set<String> strToTreeSet(String str, String split) {
		Set<String> set = Sets.newHashSet();
		if (str != null) {
			List<String> asList = Arrays.asList(str.split(split));
			for (String s : asList) {
				set.add(s);
			}
		}
		return set;
	}

	/**
	 * 取得某一时间的某一个时刻的时间
	 *
	 * @param date        某一时间
	 * @param millisecond 毫秒数
	 */
	public static String getNeedDate(Date date, long millisecond) {
		Date needDate = new Date();
		needDate.setTime(date.getTime() + millisecond);

		return dateFormat(4).format(needDate);
	}

	/**
	 * 返回接口需要的格式时间
	 *
	 * @param date date
	 * @return data string
	 */
	public static String getInterFaceTime(Date date) {
		if (date != null) {
			String dateStr = dateToString(date, 4);
			return dateStr.substring(0, 10) + "T" + dateStr.substring(11);
		} else {
			return null;
		}
	}

	/**
	 * 接口时间String 转 本地日期Date
	 *
	 * @param dateStr date string
	 * @return date
	 */
	public static Date getInterFaceToLocalTime(String dateStr) {
		if (dateStr != null) {
			dateStr = dateStr.replaceAll("T", " ");
			return Dates.stringToDate(dateStr, 4);
		} else {
			return null;
		}
	}

	/**
	 * 判断字符串是否是HH:MM的格式
	 *
	 * @param str date
	 * @return is true match hh:mm
	 */
	public static boolean isTime(String str) {
		Pattern pattern = Pattern.compile("^[0-9]{2}:[0-9]{2}");
		return pattern.matcher(str).matches();
	}

	public static Date getDateTime(String beginTime, String endTime, Date date) {

		String dateString = Dates.dateToString(date, 3);
		String fromTime = Dates.dateToString(date, 7);
		if (beginTime == null || endTime == null) {
			return date;
		}
		//检验入参的格式 是不是 HH:MM
		if (!Dates.isTime(beginTime) || !Dates.isTime(endTime)) {
			return date;

		}
		//跨天
		if (beginTime.compareTo(endTime) >= 0) {
			//当前时间小于等于结束时间，说明开始时间是前一天的
			if (fromTime.compareTo(endTime) <= 0) {
				dateString = dateString + " " + beginTime + ":00";
				return Dates.addDay(Dates.stringToDate(dateString, 4), -1);

			} else if (fromTime.compareTo(beginTime) > 0) {
				dateString = dateString + " " + beginTime + ":00";
				return Dates.stringToDate(dateString, 4);
			} else {
				return date;
			}

		} else {
			//不跨天，返回当天日期加beginTime
			dateString = dateString + " " + beginTime + ":00";
			return Dates.stringToDate(dateString, 4);
		}

	}

	/**
	 * 根据当前日期与指定时间，拼接成yyyy-mm-dd HH:mm:ss格式的新的日期
	 *
	 * @param date     当前日期
	 * @param formTime 指定时间
	 * @return yyyy-mm-dd HH:mm:ss格式的新的日期
	 */
	public static Date concatDate(Date date, String formTime) {
		StringBuilder buffer = new StringBuilder();
		String formDay = Dates.dateToString(date, 3);
		buffer.append(formDay);
		buffer.append(" ");
		if (null == date) {
			return null;
		}
		String[] timeArray = new String[3];
		if (!Strings.isNullOrEmpty(formTime)) {
			String formTimeArray[] = formTime.split(":");
			int flag = 0;
			for (int i = 0; i < formTimeArray.length; i++) {
				if (i == 3) {
					break;
				}
				timeArray[i] = formTimeArray[i];
				flag++;
			}
			for (int j = flag; j < 3; j++) {
				timeArray[j] = "00";
			}
		} else {
			for (int i = 0; i < 3; i++) {
				timeArray[i] = "00";
			}
		}
		for (int i = 0; i < timeArray.length; i++) {
			buffer.append(timeArray[i]);
			if (i == 2) {
				break;
			}
			buffer.append(":");
		}
		return stringToDate(buffer.toString(), 4);
	}

	/**
	 * 根据开始日期(YYYY-MM-DD)，结束日期(YYYY-MM-DD)，
	 * 开始时间(HH24:MM)，结束时间(HH24:MM)，判断当前时刻是否有效，注意跨天
	 *
	 * @return boolean
	 */
	public static boolean isValidDate(Date startDate, Date endDate, String beginTime, String closeTime, Date needDate) {
		if (null == startDate || null == endDate || StringUtils.isBlank(beginTime) || StringUtils.isBlank(closeTime) || null == needDate) {
			return false;
		} else {
			Date bt = stringToDate(beginTime, 7);
			Date et = stringToDate(closeTime, 7);
			Long days = Dates.dayDiff(startDate, endDate) + 1;
			if (days > 0) {
				List<ForValidDate> lstFvd = Lists.newArrayList();
				//不跨天
				if (bt.before(et)) {
					for (int i = 0; i < days; i++) {
						ForValidDate vd = new ForValidDate();
						vd.setBeginDate(concatDate(addDay(startDate, i), beginTime));
						vd.setEndDate(concatDate(addDay(startDate, i), closeTime));
						lstFvd.add(vd);
					}
				} else {
					for (int i = 0; i < days; i++) {
						ForValidDate vd = new ForValidDate();
						vd.setBeginDate(concatDate(addDay(startDate, i), beginTime));
						vd.setEndDate(concatDate(addDay(startDate, i + 1), closeTime));
						lstFvd.add(vd);
					}
				}
				if (lstFvd.isEmpty()) {
					return false;
				} else {
					boolean flag = false;
					for (ForValidDate forValidDate : lstFvd) {
						if (!needDate.before(forValidDate.getBeginDate()) && !needDate.after(forValidDate.getEndDate())) {
							flag = true;
							break;
						}
					}
					return flag;
				}
			} else {
				return false;
			}
		}
	}

	static class ForValidDate {
		Date beginDate;
		Date endDate;

		public Date getBeginDate() {
			return beginDate;
		}

		public void setBeginDate(Date beginDate) {
			this.beginDate = beginDate;
		}

		public Date getEndDate() {
			return endDate;
		}

		public void setEndDate(Date endDate) {
			this.endDate = endDate;
		}
	}
}
